package com.red.wood.parser;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.Arrays;
import java.nio.charset.Charset;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 根据IP找到相应地
 * 
 */
public class ReadIPFile implements Serializable{

	private static final long serialVersionUID = 1L;
	private static ReadIPFile instance = null;
	private static Pattern ipv4Reg = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
	
	public boolean validIpv4(String ipv4) {
		Matcher mat = ipv4Reg.matcher(ipv4);    
        return mat.find();
	}

	public static void main(String[] args) throws Exception {
		ReadIPFile rd = ReadIPFile.getInstance();
		String[] area = rd.find("255.255.255.224");
		System.out.println(area[1]);
	}

	public static synchronized ReadIPFile getInstance() throws Exception {
			if (instance == null) {
				instance = new ReadIPFile(ReadIPFile.class.getResource("/data2017.dat").getPath());
			}
		return instance;
	}
	
	private int offset;
	private int[] index = new int[256];
	private byte[] src;
	private File ipFile;

	public String[] find(String ip) {
		int ip_prefix_value = new Integer(ip.substring(0, ip.indexOf(".")));
		long ip2long_value = ip2long(ip);
		int start = index[ip_prefix_value];
		int max_comp_len = offset - 1028;
		long index_offset = -1;
		int index_length = -1;
		byte b = 0;
		for (start = start * 8 + 1024; start < max_comp_len; start += 8) {
			if (int2long(getBigianInt(subBytes(src, start + 4, start + 8))) >= ip2long_value) {
				index_offset = bytesToLong(b, src[start + 10], src[start + 9], src[start + 8]);
				index_length = 0xFF & src[start + 11];
				break;
			}
		}
		int base = offset + (int) index_offset - 1024;
		byte[] areaBytes = subBytes(src, base, base + index_length);
		return new String(areaBytes, Charset.forName("UTF-8")).split("\t", -1);
	}

	public ReadIPFile(String filename) {
		ipFile = new File(filename);
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(ipFile);
			src = new byte[(int) ipFile.length()];
//			int readBytesLength = 
					fin.read(src);
			int n = 0;
			offset = getBigianInt(subBytes(src, n, n += 4));
			int loop = 0;
			while (loop++ < 256) {
				index[loop - 1] = getEndianInt(subBytes(src, n, n += 4));
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} finally {
			try {
				if (fin != null) {
					fin.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private long bytesToLong(byte a, byte b, byte c, byte d) {
		return int2long((((a & 0xff) << 24) | ((b & 0xff) << 16) | ((c & 0xff) << 8) | (d & 0xff)));
	}

	private int str2Ip(String ip) {
		String[] ss = ip.split("\\.");
		int a, b, c, d;
		a = Integer.parseInt(ss[0]);
		b = Integer.parseInt(ss[1]);
		c = Integer.parseInt(ss[2]);
		d = Integer.parseInt(ss[3]);
		return (a << 24) | (b << 16) | (c << 8) | d;
	}

	private long ip2long(String ip) {
		return int2long(str2Ip(ip));
	}

	private long int2long(int i) {
		long l = i & 0x7fffffffL;
		if (i < 0) {
			l |= 0x080000000L;
		}
		return l;
	}

	private int getBigianInt(byte[] intBytes) {
		return (0xff & intBytes[3]) | (0xff00 & (intBytes[2] << 8)) | (0xff0000 & (intBytes[1] << 16))
				| (0xff000000 & (intBytes[0] << 24));
	}

	private int getEndianInt(byte[] intBytes) {
		return (0xff000000 & (intBytes[3] << 24)) | (0xff0000 & (intBytes[2] << 16)) | (0xff00 & (intBytes[1] << 8))
				| (0xff & intBytes[0]);
	}

	private byte[] subBytes(byte[] src, int offset, int length) {
		byte[] result = Arrays.copyOfRange(src, offset, length);
		return result;
	}
}